Passed
Push — development ( 62539b...739f83 )
by Vad
10:43 queued 14s
created

BicyclesService   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 40.82%

Importance

Changes 0
Metric Value
eloc 150
dl 0
loc 193
ccs 20
cts 49
cp 0.4082
rs 10
c 0
b 0
f 0
wmc 29

12 Functions

Rating   Name   Duplication   Size   Complexity  
A setRented 0 12 2
A findByCityAndLocation 0 11 1
A findById 0 12 2
A findAll 0 21 1
B createManyBikes 0 28 7
A findByCity 0 12 1
A update 0 5 1
B createBike 0 20 8
A findByLocation 0 6 1
A updatePositionsParallel 0 28 2
A toBicycleResponse 0 11 2
A toBicycleResponses 0 3 1
1 13
import { Injectable } from '@nestjs/common';
2 13
import { InjectRepository } from '@nestjs/typeorm';
3 13
import { In, Repository } from 'typeorm';
0 ignored issues
show
introduced by
'In' is defined but never used.
Loading history...
4 13
import { Bicycle } from './entities/bicycle.entity';
5 13
import { NotFoundException } from '@nestjs/common';
0 ignored issues
show
introduced by
'@nestjs/common' import is duplicated.
Loading history...
6
import { UpdateBicycleDto } from './dto/update-bicycle.dto';
7
import { BicycleResponse } from './types/bicycle-response.interface';
8 13
import { getDistance } from 'src/utils/geo.utils';
9
import { CreateBicycleDto } from './dto/create-bicycle.dto';
10 13
import { City } from 'src/cities/entities/city.entity';
11 13
import { CityName } from 'src/cities/types/city.enum';
12
import { BicyclePositionDto } from './dto/batch-update.dto';
13
import { BicycleBatchResponse } from './types/BicycleBatchResponse';
14
15
@Injectable()
16 13
export class BicyclesService {
17
  // ai generated code, asked to update previous function which was very inefficient
18
  async updatePositionsParallel(updates: BicyclePositionDto[]): Promise<BicycleBatchResponse[]> {
19 2
    try {
20 3
      const latitudeCases = updates.map((u) => `WHEN id = '${u.id}' THEN ${u.latitude}`).join(' ');
21
22 2
      const longitudeCases = updates
23 3
        .map((u) => `WHEN id = '${u.id}' THEN ${u.longitude}`)
24
        .join(' ');
25
26 2
      await this.bicycleRepository
27
        .createQueryBuilder()
28
        .update()
29
        .set({
30
          latitude: () => `CASE ${latitudeCases} ELSE latitude END`,
31
          longitude: () => `CASE ${longitudeCases} ELSE longitude END`,
32
        })
33
        .whereInIds(updates.map((update) => update.id))
34
        .execute();
35
36
      return updates.map((update) => ({
37
        id: update.id,
38
        success: true,
39
      }));
40
    } catch (error) {
41 3
      return updates.map((update) => ({
42
        id: update.id,
43
        success: false,
44
        error: error.message,
45
      }));
46
    }
47
  }
48
49
  constructor(
50
    @InjectRepository(Bicycle)
51 7
    private readonly bicycleRepository: Repository<Bicycle>,
52
    @InjectRepository(City)
53 7
    private cityRepository: Repository<City>,
54
  ) {}
55
56
  async findAll(): Promise<Bicycle[]> {
57
    const bikes = await this.bicycleRepository.find({
58
      relations: {
59
        city: true,
60
      },
61
      select: {
62
        id: true,
63
        batteryLevel: true,
64
        latitude: true,
65
        longitude: true,
66
        status: true,
67
        createdAt: true,
68
        updatedAt: true,
69
        city: {
70
          name: true,
71
        },
72
      },
73
    });
74
75
    return bikes;
76
  }
77
78
  async setRented(bikeId: string): Promise<Bicycle> {
79 2
    const result = await this.bicycleRepository.update(
80
      { id: bikeId, status: 'Available' },
81
      { status: 'Rented' },
82
    );
83 2
    if (result.affected === 0) {
84 1
      throw new NotFoundException(
85
        "Bike couldn't be rented. Bike might not exist or it is not available.",
86
      );
87
    }
88 1
    return await this.findById(bikeId);
89
  }
90
91
  async createBike(createBicycleDto: CreateBicycleDto): Promise<Bicycle> {
92
    const bike = this.bicycleRepository.create({
93
      batteryLevel: createBicycleDto.batteryLevel ?? 100,
94
      latitude: createBicycleDto.latitude,
95
      longitude: createBicycleDto.longitude,
96
      status: createBicycleDto.status ?? 'Available',
97
    });
98
99 2
    const city = createBicycleDto.city ?? 'Göteborg';
100
    // Find the city by name
101
    const cityEntity = await this.cityRepository.findOne({
102
      where: { name: city as CityName },
103
    });
104
105 1
    if (cityEntity) {
106
      bike.city = cityEntity;
107
    }
108
109
    return await this.bicycleRepository.save(bike);
110
  }
111
112
  async createManyBikes(createBicycleDto: CreateBicycleDto[]): Promise<Bicycle[]> {
113
    const defaultCity = await this.cityRepository.findOne({
114
      where: { name: CityName.Göteborg },
115
    });
116
    const Karlshamn = await this.cityRepository.findOne({
117
      where: { name: CityName.Karlshamn },
118
    });
119
    const Jönköping = await this.cityRepository.findOne({
120
      where: { name: CityName.Jönköping },
121
    });
122
123
    const bikes = createBicycleDto.map((bike) => {
124
      return this.bicycleRepository.create({
125
        batteryLevel: bike.batteryLevel ?? 100,
126
        latitude: bike.latitude,
127
        longitude: bike.longitude,
128
        status: bike.status ?? 'Available',
129
        city:
130
          bike.city === 'Jönköping'
131
            ? Jönköping
132
            : bike.city === 'Karlshamn'
133
              ? Karlshamn
134
              : defaultCity,
135
      });
136
    });
137
138
    return await this.bicycleRepository.save(bikes);
139
  }
140
141
  async findById(id: string): Promise<Bicycle> {
142
    const bike = await this.bicycleRepository.findOne({
143
      where: { id },
144
      relations: {
145
        city: true,
146
      },
147
    });
148 1
    if (!bike) {
149
      throw new NotFoundException('Bike not found');
150
    }
151
    return bike;
152
  }
153
154
  async update(id: string, updateBicycleDto: UpdateBicycleDto): Promise<Bicycle> {
155
    const bike = await this.findById(id);
156
157
    return this.bicycleRepository.save({ ...bike, ...updateBicycleDto });
158
  }
159
160
  async findByCity(cityName: CityName): Promise<Bicycle[]> {
161
    const bikes = await this.bicycleRepository.find({
162
      where: {
163
        city: {
164
          name: cityName,
165
        },
166
      },
167
      relations: ['city'],
168
    });
169
170
    return bikes;
171
  }
172
  async findByLocation(lat: number, lon: number, radius: number): Promise<Bicycle[]> {
173
    const allBikes = await this.findAll();
174
    const filteredBikes = allBikes.filter((bike) => {
175
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
176
    });
177
    return filteredBikes;
178
  }
179
  async findByCityAndLocation(
180
    city: any,
181
    lat: number,
182
    lon: number,
183
    radius: number,
184
  ): Promise<Bicycle[]> {
185 2
    const bikesInCity = await this.findByCity(city);
186 2
    const filteredBikes = bikesInCity.filter((bike) => {
187 1
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
188
    });
189 2
    return filteredBikes;
190
  }
191
192
  private toBicycleResponse(bike: Bicycle): BicycleResponse {
193
    return {
194
      id: bike.id,
195
      batteryLevel: bike.batteryLevel,
196
      latitude: bike.latitude,
197
      longitude: bike.longitude,
198
      status: bike.status,
199
      city: bike.city?.name,
200
      createdAt: bike.createdAt,
201
      updatedAt: bike.updatedAt,
202
    };
203
  }
204
205
  toBicycleResponses(bikes: Bicycle[]): BicycleResponse[] {
206
    return bikes.map((bike) => this.toBicycleResponse(bike));
207
  }
208
}
209